#!/usr/bin/env bash
# Filename: bondtool

####################################################
# bond 脚本，支持删除，多路由支持，列表操作，支持
# Dialog界面配置bond。
# Author: razone <zoujichun3166@gmail.com>
####################################################

modconf=/etc/modprobe.conf
netconf=/etc/sysconfig/network
confdir=/etc/sysconfig/network-scripts

usage(){
    local cmd
    if test -n "$DISPLAY" && echo $LANG | grep "zh_CN" &>/dev/null; then
        ## 中文帮助信息
        if echo $LANG | grep '[gb,GB]' &>/dev/null; then
            cmd="iconv -f utf8 -t gbk"
        else
            cmd=cat
        fi
        ##
        $cmd << EOF
        `basename $0` [命令] [设备名称] [参数]

        命令：
        [create|-c]        创建bond 设备
        [delete|-d]        删除bond 设备
        [list|-l]          列出系统已有的bond 设备
        [edit|-e]          配置bond 设备的IP地址，网关，路由信息
        [hostname|-h]      修改主机名
        [restart|-r]       重启网络
        [tui|-t]           字符图形界面设置bond
        [help|-H]          查看此文档

        设备名称
        bond0, bond1, 等等

        参数
        slave 设备名称，IP地址，网关，bondmode（bond模式），主机名，等等

        例子
        创建一个bond 设备，模式省略的话为1（主备模式）：       `basename $0` -c bond0 eth0 eth1 1 
        删除一个bond 设备：                                    `basename $0` -d bond0
        删除所有bond 设备：                                    `basename $0` -d all
        查看已有的bond 设备：                                  `basename $0` -l 
        查看指定设备信息：                                     `basename $0` -l bond0
        重启网络：                                             `basename $0` -r
        利用dialog界面配置bond：                               `basename $0` -t
        修改主机名：                                           `basename $0` -h hostname
        修改bond IP地址：                                      `basename $0` -e bond0 ip 192.168.100.10
        修改bond 子网掩码：                                    `basename $0` -e bond0 nm 255.255.255.0
        修改bond 网关：                                        `basename $0` -e bond0 gw 192.168.100.1
        配置bond 设备IP等信息：                                `basename $0` -e bond0 ip 192.168.100.10 nm 255.255.255.0 gw 192.168.100.1
EOF
    else
        ## 英文帮助信息
        cat <<EOF
        `basename $0` [command] [devicename] [parameter]
    
        command 
    
        [create|-c]    create a bond device
        [delete|-d]    delete a bond device
        [list|-l]      list all bond devices
        [edit|-e]      edit the bond device's ipaddress
        [hostname|-h]  change the hostname
        [restart|-r]   restart network
        [tui|-t]       use dialog to configure bond
        [help|-H]      print this messages
    
        devicename
        bond0, bond1, etc
    
        parameter
        slave devices, ipadrees, netmask, gateway, hostname, bondmode, etc
    
        examples
        create a bond device:       `basename $0` -c bond0 eth0 eth1 1
        delete a bond device:       `basename $0` -d bond0
        delete all bond devices:    `basename $0` -d all
        list all bond devices:      `basename $0` -l 
        list bond0:                 `basename $0` -l bond0
        restart network:            `basename $0` -r
        use dialog to configure:    `basename $0` -t
        change the hostname:        `basename $0` -h hostname
        change the bond ipadress:   `basename $0` -e bond0 ip 192.168.100.10
        change the bond netmask:    `basename $0` -e bond0 nm 255.255.255.0
        change the bond gateway:    `basename $0` -e bond0 gw 192.168.100.1
        edit bond0's ip:            `basename $0` -e bond0 ip 192.168.100.10 nm 255.255.255.0 gw 192.168.100.1
EOF
    fi
    exit 1
}

error_msg(){
    local msg
    msg=$1
    echo -e "`basename $0`: ERROR:: $msg"
}

ok_msg(){
    local msg
    msg=$1
    echo -e "`basename $0`: SUCCESS:: $msg"
}

warn_msg(){
    local msg
    msg=$1
    echo -e "`basename $0`: WARNNING:: $msg"
}

bond_create(){
    local bonddevice slave1 slave2 bondmode
    bonddevice=$1
    slave1=$2
    slave2=$3
    bondmode=$4
    ## bond名称合法性检测
    if echo $bonddevice | grep '[bond][0-9]\{1,\}' &>/dev/null; then
        :
    else
        error_msg "Please use the right parameter, like bond*"
        exit 1
    fi
    ## 检查是否已存在或者已使用
    if  grep "$bonddevice" $modconf &>/dev/null; then
        error_msg "$bonddevice was already existed! Please delete it first!"
        error_msg "You can run this:  `basename $0` -d $bonddevice"
        exit 1
    fi
    ## 检查slave 设备
    for i in "$slave1" "$slave2"; do
        ## 名称判断
        if echo $i | grep '[eth,ib][0-9]\{1,\}' &>/dev/null; then
            :
        else
            error_msg "Please use the right parameter, like eth*, ib*"
            exit 1
        fi
        ## 是否已使用
        if grep "SLAVE" $confdir/ifcfg-$i &>/dev/null; then
            error_msg "$i was already slave!"
            exit 1
        fi
    done
    ## 检查模式是否正确
    if test -z "$bondmode" || echo $bondmode | grep '[0-6]' &>/dev/null; then
        :
    else
        error_msg "Please use the right bondmode, like [0-6]."
        exit 1
    fi
    ## 生成配置文件
    echo -e "DEVICE=$bonddevice\
        \nONBOOT=yes \
        \nBOOTPROTO=none \
        \nUSERCTL=no" > $confdir/ifcfg-$bonddevice
    echo -e "DEVICE=$slave1\
        \nHWADDR=`ifconfig $slave1 | grep HWaddr | awk '{print $5}'` \
        \nUSERCTL=no \
        \nONBOOT=yes \
        \nMASTER=$bonddevice \
        \nSLAVE=yes \
        \nBOOTPROTO=none" > $confdir/ifcfg-$slave1
    echo -e "DEVICE=$slave2 \
        \nHWADDR=`ifconfig $slave2 | grep HWaddr | awk '{print $5}'` \
        \nUSERCTL=no \
        \nONBOOT=yes \
        \nMASTER=$bonddevice \
        \nSLAVE=yes \
        \nBOOTPROTO=none" > $confdir/ifcfg-$slave2

    if ! grep 'alias.*bond[0-9].*bonding' $modconf &>/dev/null; then
        if ! grep 'modprobe.*bonding.*bond' $modconf &>/dev/null; then
            echo -e "alias $bonddevice bonding \
                \noptions $bonddevice miimon=100 mode=${bondmode:=1}" >> $modconf
        else
            echo "install $bonddevice /sbin/modprobe --ignore-install \
                bonding -o $bonddevice miimon=100 mode=${bondmode:=1}" >> $modconf
        fi
    else
        oribonddev=`grep 'alias.*bond[0-9]' $modconf | awk '{print $2}'`
        oribondmode=`grep 'options.*bond[0-9]' $modconf | awk '{print $4}'`
        sed -i '/alias.*bond[0-9]/d;/options.*bond/d' $modconf
        if [ -z $oribonddev -a -z $oribondmode ]; then
            echo "install $bonddevice /sbin/modprobe --ignore-install \
                bonding -o $bonddevice miimon=100 mode=${bondmode:=1}" >> $modconf
        else
            echo "install $oribonddev /sbin/modprobe --ignore-install \
                bonding -o $oribonddev miimon=100 $oribondmode" >> $modconf
            echo "install $bonddevice /sbin/modprobe --ignore-install \
                bonding -o $bonddevice miimon=100 mode=${bondmode:=1}" >> $modconf
        fi
    fi
    ok_msg "$bonddevice was created successful!"
}

bond_delete(){
    local num rmbond oribonddev oribondmode ops
    rmbond=$1
    ## 名称合法性检查
    if echo $rmbond | grep '\<all\>' &>/dev/null || echo $rmbond | grep '[bond][0-9]\{1,\}' &>/dev/null; then
        :
    else
        error_msg "Please use the right parameter, like bond*, all"
        exit 1
    fi
    ## 确认指定bond 设备是否存在
    if test "$rmbond" = "all" || grep "$rmbond" $modconf &>/dev/null; then
        :
    else
        error_msg "$rmbond does not exist!"
        exit 1
    fi
    ## 确认删除所有配置
    warn_msg "Will delete $rmbond, continue(y/n)?"
    read ops
    if [ "$ops" = "y" -o "$ops" = "Y" ]; then
        :
    else
        exit 0
    fi
    ## 删除所有配置
    if [ "$rmbond" = "all" ]; then
        rmbond="bond"
        sed -i "/bond[0-9]/d" $modconf
        rm -f $confdir/ifcfg-$rmbond*
        # 删除路由
        rm -f $confdir/route-$rmbond
        for i in `grep -l "$rmbond" $confdir/ifcfg-*`; do
            sed -i "/$rmbond/d;/SLAVE/d;s/ONBOOT=yes/ONBOOT=no/g" $i
        done
        ## 清除 /etc/sysconfig/network
        sed -i "/GATEWAY/d" $netconf
        ok_msg "ALL bond devices were deleted successful!"
        exit 0
    fi

    ## 测试bond 个数
    if grep 'alias.*bond[0-9].*bonding' $modconf &>/dev/null; then
        num=1
    elif grep 'modprobe.*bonding.*bond' $modconf &>/dev/null; then
        num=`grep 'modprobe.*bonding.*bond' $modconf  | wc -l`
    else
        num=0
    fi

    ## 删除 /etc/modprobe.conf bond 配置
    if [ $num -gt 2 -o $num -eq 1 ]; then
        sed -i "/$rmbond/d" $modconf
        sed -i "/GATEWAY/d" $netconf
        rm -f $confdir/route-$rmbond
    elif [ $num -eq 2 ]; then
        ## 删除/etc/modprobe.conf 关于bond1 的配置
        sed -i "/$rmbond/d" $modconf
        oribonddev=`grep 'modprobe.*bonding.*bond' $modconf | awk '{print $2}'`
        oribondmode=`grep 'modprobe.*bonding.*bond' $modconf | awk '{print $9}'`
        sed -i '/modprobe.*bonding.*bond/d' $modconf
        echo -e "alias bond0 bonding\noptions bond0 miimon=100 $oribondmode" >> $modconf
        ## 删除网关     
        sed -i "/GATEWAY/d" $netconf
        rm -f $confdir/route-$rmbond
        ## 删除 /etc/sysconfig/network-scripts/ 里面配置文件，如果是bond1 则修改为 bond0
        if [ "$rmbond" = "bond0" ]; then
            for i in `grep -l "$rmbond" $confdir/ifcfg-[e,i]*`; do
                sed -i "/$rmbond/d;/SLAVE/d;s/ONBOOT=yes/ONBOOT=no/g" $i
            done
            # 路由信息
            rm -f $confdir/route-bond0
            mv -f $confdir/route-$oribonddev $confdir/route-bond0
            # 网络信息
            rm -f $confdir/ifcfg-bond0
            mv -f $confdir/ifcfg-$oribonddev $confdir/ifcfg-bond0
            sed -i "s/$oribonddev/bond0/g" $confdir/ifcfg-bond0
            for i in `grep -l "$oribonddev" $confdir/ifcfg-[e,i]*`; do
                sed -i "s/$oribonddev/bond0/g" $i
            done
            ok_msg "$rmbond was deleted successful!"
            exit 0
        else
            rm -f $confdir/ifcfg-$rmbond
            rm -f $confdir/route-$rmbond
            for i in `grep -l "$rmbond" $confdir/ifcfg-[e,i]*`; do
                sed -i "/$rmbond/d;/SLAVE/d;s/ONBOOT=yes/ONBOOT=no/g" $i
            done
        fi
    else
        error_msg "No bond device was configured!"
    fi

    ## 清理网卡配置文件
    for i in `grep -l "$rmbond" $confdir/ifcfg-*`; do
        sed -i "/$rmbond/d;/SLAVE/d;s/ONBOOT=yes/ONBOOT=no/g" $i 
    done
    rm -f $confdir/ifcfg-$rmbond
    ok_msg "$rmbond was deleted successful!"
}

bond_list(){
    local bonddev ip nm gw defaultgw
    bonddev=$1
    ##名称合法性检查，可以为空
    if test -z "$bonddev" || echo $bonddev | grep '[bond][0-9]\{1,\}' &>/dev/null \
        || echo $bonddev | grep '\<all\>' &>/dev/null; then
        :
    else
        error_msg "Please use the right parameter, like bond*, all"
        exit 1
    fi
    ##
    if [ -z "$bonddev" -o "$bonddev" = "all" ]; then
        bonddev="bond*"
    fi
    if grep "$bonddev" $modconf &>/dev/null; then
        cd $confdir
        allbond=`ls ifcfg-$bonddev | sed 's/ifcfg-//g'`
        defaultgw=`grep "GATEWAY" $netconf | awk -F"=" '{print $2}'`
        echo -e "Default GATEWAY:: $defaultgw\n"
        for i in $allbond; do
            ip=`grep "IPADDR=" ifcfg-$i | awk -F"=" '{print $2}' | sed "s/[ ]*//g"`
            nm=`grep "NETMASK=" ifcfg-$i | awk -F"=" '{print $2}' | sed "s/[ ]*//g"`
            gw=`test -f route-$i && awk '{print $3}' route-$i`
            slaves=`grep -l "\<$i\>" ifcfg-[e,i]* | sed 's/ifcfg-/    --/g;s/$/\ \<slave\>/g'`
            echo -e "$i <Master>:: <ip $ip> <netmask $nm>"
            echo -e "                 <gateway $gw>\n$slaves\n"
        done
    else
        error_msg "No bond device found!"
    fi
}

## ip地址，子网掩码，网关合法性检验
check_ip(){
    if echo $1 | grep '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' &>/dev/null; then
        :
    else
        error_msg "Please use the right parameter, like xxx.xxx.xxx.xxx"
        exit 1
    fi
}

bond_edit(){
    local device network ip netmask ops prefix
    device=$1
    ## device 参数检查
    if echo $device | grep '[bond,eth,ib][0-9]\{1,\}' &>/dev/null; then
        :
    else
        error_msg "Please use the right parameter, like bond*, eth*, ib*"
        exit 1
    fi
    ## ip地址，子网掩码，网关参数检查
    if echo $2 | grep '[ip,nm,mask,netmask,gw,gateway]' &>/dev/null; then
        :
    else
        usage
    fi
    ##
    while [ $# -ge 1 ]; do
        case $2 in
            ip)
                check_ip $3
                sed -i "/IPADDR/d" $confdir/ifcfg-$device
                echo "IPADDR=$3" >> $confdir/ifcfg-$device
                ok_msg "$device: <IPADDRESS $3>"
                shift
                ;;
            nm|netmask|mask)
                check_ip $3
                sed -i "/NETMASK/d" $confdir/ifcfg-$device
                echo "NETMASK=$3" >> $confdir/ifcfg-$device
                ok_msg "$device: <NETMASK $3>"
                shift
                ;;
            gw|gateway)
                check_ip $3
                if grep "GATEWAY=.[0-9]\{1,\}" $netconf &>/dev/null; then
                    warn_msg "Default gateway is already existed!"
                    warn_msg "Do you want overwrite it?[y/a/q]"
                    warn_msg "y:overwrite, a:add another route, q:quit"
                    read ops
                    case $ops in 
                        y|Y)
                            sed -i "s/GATEWAY.*$/GATEWAY=$3/g" $netconf
                            ok_msg "$device: <GATEWAY $3> <conf $netconf>"
                            ;;
                        a|A)
                            ip=`grep "IPADDR=" $confdir/ifcfg-$device | awk -F"=" '{print $2}'`
                            netmask=`grep "NETMASK=" $confdir/ifcfg-$device | awk -F"=" '{print $2}'`
                            if [ -n "$ip" -a -n "$netmask" ]; then
                                network=`ipcalc -n $ip $netmask | awk -F"=" '{print $2}'`
                                prefix=`ipcalc -p $ip $netmask | awk -F"=" '{print $2}'`
                                echo "$network/$prefix via $3" > $confdir/route-$device
                                ok_msg "<GATEWAY $3> <conf $confdir/route-$device>"
                            else
                                error_msg "You should configure ipaddress first!"
                                exit 1
                            fi
                            ;;
                        q|*)
                            exit 1
                            ;;
                    esac
                else
                    echo "GATEWAY=$3" >> $netconf
                    ok_msg "$device: <GATEWAY $3> <conf $netconf>"
                fi
                shift
                ;;
            *)
                shift
                ;;
        esac
    done
}

bond_dialog(){
    local DIALOG operation_status init_input bondlist returncode bondnum menu_result
    DIALOG=dialog
    returncode=0
    while [ "$returncode" != "1" ]; do
        exec 3>&1
        menu_result=`$DIALOG  --cancel-label "Exit" --ok-label "OK" --title "Redflag Bond Manager" \
            --menu "Take an operation" 13 50 5 \
            1 "Create bond device" \
            2 "List bond device" \
            3 "Delete all bond device" \
            4 "Restart network" \
            5 "Exit"\
            2>&1 1>&3`
            returncode=$?
            exec 3>&-

            case $menu_result in
                1)
                    init_input=`mktemp`
                    # 自动生成bond 设备名称
                    bondnum=`grep -o "bond[0-9]" $modconf  | grep -o "[0-9]" | sort -n | tail -n 1`
                    if [ -z "$bondnum" ]; then
                        bonddevice=bond0
                    else
                        bonddevice=bond$(expr $bondnum + 1)
                    fi
                    $DIALOG --cancel-label "Back" --ok-label "OK" --title "Redflag Bond Manager" \
                        --form "Create bond device" 20 45 13 \
                        "bonddevice:      " 1 1 "$bonddevice" 1 16 6 5 \
                        "slave1:          " 3 1 "" 3 16 6 4 \
                        "slave2:          " 5 1 "" 5 16 6 4 \
                        "bondmode:        " 7 1 "1" 7 16 2 1 \
                        "ipaddress:       " 9 1 "" 9 16 20 15 \
                        "netmask:         " 11 1 "" 11 16 20 15 \
                        "gateway:         " 13 1 "" 13 16 20 15 2>$init_input
                        # 参数取出
                        if [ $? = 0 ]; then
                            bonddevice=`sed -ne '1p' $init_input`
                            slave1=`sed -ne '2p' $init_input`
                            slave2=`sed -ne '3p' $init_input`
                            bondmode=`sed -ne '4p' $init_input`
                            ip=`sed -ne '5p' $init_input`
                            netmask=`sed -ne '6p' $init_input`
                            gw=`sed -ne '7p' $init_input`
                            bond_create $bonddevice $slave1 $slave2 $bondmode
                            if [ -z "$gw" ]; then
                                bond_edit $bonddevice ip $ip nm $netmask
                            else
                                bond_edit $bonddevice ip $ip nm $netmask gw $gw
                            fi
                        fi
                        rm -f $init_input
                        ;;
                    2)
                        bondlist=`mktemp`
                        bond_list all > $bondlist
                        $DIALOG --exit-label "Back" --title "Redflag Bond Manager" --textbox $bondlist 20 70
                        rm -f $bondlist
                        ;;
                    3)
                        bond_delete all
                        ;;
                    4)
                        restart_network
                        ;;
                    5|*)
                        exit 1
                        ;;
                esac
            done
}

restart_network(){
    local ops
    warn_msg "System will restart network, continue(y/n)?"
    read ops
    if [ "$ops" = "y" -o "$ops" = "yes" -o "$ops" = "Y" ]; then
        warn_msg "Restarting network!"
        for tempmod in $(lsmod | grep -i bond | awk '{print $1}'); do
            modprobe -r bonding -o "$tempmod"
        done
        /etc/init.d/network restart &>/dev/null
        ok_msg "Restarting network OK!"
    else
        exit 1
    fi
    }

################################### 主程序
## 权限检查
if [ $UID != 0 ]; then
    error_msg "Please run this script as root!"
    exit 1
fi
## 参数越界检查
if [ $# -gt 9 ]; then
    usage
fi

case "$1" in
    create|-c)
        bond_create $2 $3 $4 $5
        ;;
    delete|-d)
        bond_delete $2
        ;;
    list|ls|-l)
        bond_list $2
        ;;
    edit|-e)
        bond_edit $2 $3 $4 $5 $6 $7 $8
        ;;
    -h|hostname|hn)
        if [ -z "$2" ]; then
            error_msg "Need a hostname!"
            exit 1
        fi
        sed -i "/HOSTNAME/d" $netconf
        echo "HOSTNAME=$2" >> $netconf
        hostname $2
        ok_msg "HOSTNAME: $2"
        exit 0
        ;;
    -t|tui)
        bond_dialog
        ;;
    restart|-r) ## 重启网络
        if [ -z "$2" ]; then
            restart_network
        else
            usage
        fi
        ;;
    bond*) ## 兼容旧的操作方式 bondtool bond0 eth0 eth1 192.168.100.10 255.255.255.0 192.168.100.1
        bond_create $1 $2 $3
        bond_edit $1 ip $4 nm $5 gw $6
        restart_network
        ;;
    -H|--help|help|*)
        usage
        ;;
esac
